﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
//using WQLAPI.Logs;
//using WQLAPI.Extensions;
using System.IO.Ports;
using System.IO;
using System.Threading.Tasks;
using XModem_Project;
using System.Windows.Forms;
#if NET35 || NET45
using System.IO.Ports;
#endif
#if NET45
using System.Threading.Tasks;
#endif

//#if NET35 || NET45
namespace WQLAPI.Helper
{
    /// <summary>
    /// 串口帮助类
    /// </summary>
    public class SerialPortHelper
        //: Logger
    {
        private object _rLock = new object();//读锁
        private object _wLock = new object();//写锁

        private SerialPort _serialPort;
        /// <summary>
        /// 串口实例
        /// </summary>
        public SerialPort SerialPort
        {
            get
            {
                return _serialPort;
            }
            private set
            {
                _serialPort = value;
            }
        }
        /// <summary>
        /// 编码
        /// </summary>
        public Encoding Encoding
        {
            get { return _serialPort.Encoding; }
            set { _serialPort.Encoding = value; }
        }

        /// <summary>
        /// 编码
        /// </summary>
        public string NewLine
        {
            get { return _serialPort.NewLine; }
            set { _serialPort.NewLine = value; }
        }
        /// <summary>
        /// 构造方法
        /// </summary>
        public SerialPortHelper()
        {
            _serialPort = new SerialPort();
        }
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="serialPort">串口对象</param>
        public SerialPortHelper(SerialPort serialPort)
        {
            _serialPort = serialPort;

        }
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="portName">串口名</param>
        /// <param name="baudRate">比特率</param>
        /// <param name="dataBit">位数</param>
        /// <param name="stopBits">停止位</param>
        /// <param name="parity">校验</param>
        public SerialPortHelper(string portName, int baudRate, int dataBit = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None)
        {
            _serialPort = new SerialPort();
            Init(portName, baudRate, dataBit, stopBits, parity);

        }
        /// <summary>
        /// 初始化串口参数
        /// </summary>
        /// <param name="portName">端口</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="dataBit">数据位</param>
        /// <param name="stopBits">停止位</param>
        /// <param name="parity">校验位</param>
        public void Init(string portName, int baudRate, int dataBit, StopBits stopBits, Parity parity)
        {
            _serialPort.PortName = portName;
            _serialPort.BaudRate = baudRate;
            _serialPort.DataBits = dataBit;
            _serialPort.StopBits = stopBits;
            _serialPort.Parity = parity;

        }
        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            if (_serialPort.IsOpen)
            {
                return true;
            }
            else
            {
                try
                {
                    _serialPort.Open();
                    //OnLogInfoEvent("串口打开成功");
                    return true;
                }
                catch (Exception ex)
                {
                    //OnLogErrorEvent("打开串口失败" + ex.Message);
                    return false;
                }
            }
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            if (!_serialPort.IsOpen)
            {
                //OnLogInfoEvent("串口未打开");
                return false;
            }
            else
            {
                try
                {
                    _serialPort.Close();
                    //OnLogInfoEvent("串口关闭成功");
                    return true;
                }
                catch (Exception ex)
                {
                    //OnLogErrorEvent("关闭串口失败" + ex.Message);
                    return false;
                }
            }
        }
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data">发送数据</param>
        /// <param name="newLine">是否作为加换行符发送</param>
        /// <returns></returns>
        public bool SendData(byte[] data, bool newLine = true)
        {
            try
            {
                lock (_wLock)
                {
                    if (newLine)
                    {
                        var list = data.ToList();
                        list.AddRange(Encoding.ASCII.GetBytes(NewLine));
                        data = list.ToArray();
                    }
                    //OnLogDebugEvent("发送：" + data);
                    _serialPort.DiscardInBuffer();
                    _serialPort.Write(data, 0, data.Length);
                    return true;

                }

            }
            catch (Exception ex)
            {
                //OnLogErrorEvent("发送数据失败" + ex.Message);
                return false;
            }
        }
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data">发送数据</param>
        /// <param name="newLine">是否作为加换行符发送</param>
        public bool SendData(string data, bool newLine = true)
        {
            try
            {
                lock (_wLock)
                {
                    _serialPort.DiscardInBuffer();
                    if (newLine)
                    {
                        _serialPort.WriteLine(data);
                    }
                    else
                    {
                        _serialPort.Write(data);
                    }
                    //OnLogDebugEvent("发送：" + data);
                    return true;
                }

            }
            catch (Exception ex)
            {
                //OnLogErrorEvent("发送数据失败" + ex.Message);
                return false;
            }
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <returns></returns>
        public string ReadData()
        {
            var str = Encoding.GetString(ReadByte());
            //OnLogDebugEvent("接受(string)：" + str);
            return str;
        }
        /// <summary>
        ///  一直读取到输入缓冲区中的指定 value 的字符串。
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string ReadData(string value)
        {
            lock (_rLock)
            {
                var str = _serialPort.ReadTo(value);
                //OnLogDebugEvent("接受(string)：" + str);
                return str;
            }

        }
        /// <summary>
        /// 读取byte数据
        /// </summary>
        /// <returns></returns>
        public byte[] ReadByte()
        {
            try
            {
                lock (_rLock)
                {
                    var len = _serialPort.BytesToRead;
                    byte[] array = new byte[len];
                    _serialPort.Read(array, 0, len);
                    //OnLogDebugEvent("接受(bytes)：" + array.ToHexString());
                    return array;
                }

            }
            catch (Exception ex)
            {
                //OnLogErrorEvent("读取失败" + ex.Message);
                return null;
            }
        }
        /// <summary>
        /// 读写数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="newLine">是否发送newline</param>
        /// <returns>字节数组</returns>
        public byte[] SendAndRead(byte[] data, bool newLine = true)
        {
            if (SendData(data, newLine))
            {
                return ReadByte();
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// 发送并读取数值
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="newLine">是否发送newline</param>
        /// <param name="value">是否一直等待读到value为止，为空则不等待</param>
        /// <returns></returns>
        public string SendAndRead(string cmd, bool newLine = true, string value = "\r")
        {
            if (SendData(cmd, newLine))
            {
                if (string.IsNullOrEmpty(value))
                {
                    return ReadData(value);
                }
                else
                {
                    return ReadData();
                }
            }
            else
            {
                return null;
            }
        }

        #region
        crc16 Crc_o = new crc16();

        byte[] Sender_Packet = new byte[3];
        byte[] Sender_Data = new byte[1024];
        byte[] Sender_Crc = new byte[2];
        byte[] Sender_EOT = new byte[1];
        ushort Sender_Crc_us;

        //SerialPort SPort;

        byte Sender_Packet_Number = 0;
        public void xmodem_send(string filepaths)
        {
            FileStream fs = File.Open(filepaths, FileMode.Open);
            BinaryReader B_reader = new BinaryReader(fs);
            int err = 0;
            string Read_line;
            Sender_Packet_Number = 1;
            this.wait_c();

            Sender_Data = B_reader.ReadBytes(1024);
            err = Send_Packet(Sender_Data, Sender_Packet_Number, 1024);
            while (true)
            {
                if (err == 1)
                {
                    Sender_Data = B_reader.ReadBytes(1024);

                    if (Sender_Data.Length == 0)
                    {
                        _serialPort.Write(Sender_EOT, 0, 1);
                        while (true)
                        {
                            Read_line = _serialPort.ReadLine();
                            Console.Write(Read_line);
                        }

                        //break;
                    }
                    else if (Sender_Data.Length != 1024)
                    {
                        byte[] full_stream = new byte[1024];
                        byte[] zero_ary = new byte[1024 - Sender_Data.Length];
                        Array.Clear(zero_ary, 0, zero_ary.Length);

                        Console.WriteLine(Sender_Data.Length);

                        Array.Copy(Sender_Data, 0, full_stream, 0, Sender_Data.Length);
                        Array.Copy(zero_ary, 0, full_stream, Sender_Data.Length, zero_ary.Length);

                        Sender_Data = full_stream;
                    }

                    Sender_Packet_Number++;
                    err = Send_Packet(Sender_Data, Sender_Packet_Number, 1024);
                }
                else
                {
                    err = Send_Packet(Sender_Data, Sender_Packet_Number, err);
                }
            }

        }

        /*
         * XModem 시작 문자 대기 'C'
         */

        static class Constants
        {
            public const byte SOH = 0x01;
            public const byte SOX = 0x02;
            public const byte EOT = 0x04;
            public const byte ETB = 0x17;

            public const byte ACK = 0x06;
            public const byte NAK = 0x15;

            public const byte CAN = 0x18;
        }
        private void wait_c()
        {
            char readchar;
            while (true)
            {
                readchar = (char)_serialPort.ReadChar();
                Console.WriteLine(readchar);
                if (readchar == 'C')
                {
                    break;
                }
            }
        }

        /********************************************************
		 * send 형식 바꿀 때는 이 곳을 변경
         * make packet and send
         */
        private int Send_Packet(byte[] data, byte SPN, int Length)
        {
            if (Length == 128)
            {
                Sender_Packet[0] = Constants.SOH;
            }
            else if (Length == 1024)
            {
                Sender_Packet[0] = Constants.SOX;
            }
            Sender_Packet[1] = SPN;

            /*byte -> int 형변환 때문에 더하기 빼기도 어렵네...*/
            //Sender_Packet[2] = BitConverter.GetBytes(255 - SPN)[0];
            Sender_Packet[2] = (byte)(~SPN);

            Console.WriteLine(BitConverter.ToString(Sender_Packet));
            _serialPort.Write(Sender_Packet, 0, 3);

            Console.WriteLine(BitConverter.ToString(data));
            _serialPort.Write(data, 0, Length);

            //Sender_Crc = crc16.ComputeCrc(data);
            //Sender_Crc = Crc_o.calcrc(data);
            Sender_Crc_us = crc16.CRC16_ccitt(data, 0, Length);
            Sender_Crc[0] = (byte)((Sender_Crc_us >> 8) & 0xFF);
            Sender_Crc[1] = (byte)(Sender_Crc_us & 0xFF);

            Console.WriteLine(BitConverter.ToString(Sender_Crc));
            _serialPort.Write(Sender_Crc, 0, 2);

            return Wait_ACK_NAK(Length);
        }

        private int Wait_ACK_NAK(int Length)
        {
            int SPort_read;

            while (true)
            {
                Console.WriteLine("Wait_ACK_NAK");
                SPort_read = _serialPort.ReadChar();//Console.Read();

                if (SPort_read == Constants.NAK) 		//Constants.NAK
                {
                    Console.WriteLine("NAK");
                    return Length;
                }
                else if (SPort_read == Constants.ACK) 	//Constants.ACK
                {
                    Console.WriteLine("ACK");
                    return 1;
                }
                else
                {
                    Console.WriteLine(SPort_read);
                }
            }
        }
        #endregion

        public Dictionary<int, List<byte>> ReadBytes(string filePaths)
        {
            
            FileStream fs = new FileStream(filePaths, FileMode.Open, FileAccess.Read);

            int i = 1;
            List<byte> bytePackge = new List<byte>();
            Dictionary<int, List<byte>> dic = new Dictionary<int, List<byte>>();
            byte[] by = new byte[1024];
            using (BinaryReader reader = new BinaryReader(fs))
            {

                while (reader.BaseStream.Position < reader.BaseStream.Length)
                {
                    byte by2 = reader.ReadByte();

                    bytePackge.Add(by2);
                    if (bytePackge.Count >= 1024)
                    {
                        bytePackge.CopyTo(by);
                        dic[i] = by.ToList();
                        bytePackge.Clear();
                        i++;
                    }
                }
            }
            if (bytePackge.Count != 0)
            {

                for (int s = bytePackge.Count; s < 1024; s++)
                {
                    bytePackge.Add(0x1A);
                    
                }
                dic[i] = bytePackge;
            }
            return dic;
        }
        //int i;
        public void SendBytes(string filePaths)
        {
            //byte[] byss = new byte[1024];
            byte[] buff = new byte[1024];
            Dictionary<int, List<byte>> dics = new Dictionary<int, List<byte>>();
            dics = ReadBytes(filePaths);
            Console.WriteLine(dics.Count);
            byte Sender_Packet_Number = 1;
            for (int i = 1; i <= dics.Count; i++)
            {
                //SendPacket(dics[i].ToArray(),Convert.ToUInt16(i));
                SendPacket(dics[i].ToArray(), Sender_Packet_Number);
                //Task.Delay(1000);
                Sender_Packet_Number++;

               
                if (buff.Length > 0)
                {
                    int s = 0;
                    while(true)
                    {
                        Thread.Sleep(50);
                        if (_serialPort.BytesToRead > 0)
                        {
                           int c= _serialPort.Read(buff, 0, buff.Length);
                            Console.WriteLine(c + ":" + buff[0].ToString());
                            if (buff[0] == 0x06)
                            {
                                //MessageBox.Show(buff[s].ToString());
                                //Console.WriteLine(buff[s].ToString() + ":" + Encoding.Default.GetString(buff));
                                Console.WriteLine("06:"+c + ":" + buff[0].ToString()+":" + Encoding.Default.GetString(buff));
                                break;

                            }
                            
                            else if (buff[0] == 0x15)
                            {
                                i--;
                                Console.WriteLine("15:" + c + ":" + buff[0].ToString());
                                break;
                            }
                            else if (buff[0] == 0x18)
                            {
                                Console.WriteLine("18:" + c + ":" + buff[0].ToString());
                                Console.WriteLine(Encoding.Default.GetString(buff));
                                break;
                            }
                            
                            Console.WriteLine(c+":"+buff[0].ToString());
                        }
                       
                        s++;
                    }
                    
                    //if (buff[0] != 0x06)
                    //{
                    //    MessageBox.Show(buff[0].ToString());
                    //    break;

                    //}else if(buff[0] == 0x15)
                    //{
                    //    i--;
                    //}
                    //else if(buff[0] == 0x18)
                    //{
                    //    break;
                    //}
                    //ss = Encoding.GetEncoding("utf-8").GetString(buff);
                }
                Console.WriteLine(i);


            }

            _serialPort.Write(new byte[] { 0x04 }, 0, 1);
            Thread.Sleep(50);
            //if (_serialPort.BytesToRead > 0)
            //{
            //    int c = _serialPort.Read(buff, 0, buff.Length);
            //    Console.WriteLine(c + ":" + buff[0].ToString()+":"+ Encoding.Default.GetString(buff));
            //    //if (buff[0] == 0x06)
            //    //{
            //    //    //MessageBox.Show(buff[s].ToString());
            //    //    //Console.WriteLine(buff[s].ToString() + ":" + Encoding.Default.GetString(buff));
            //    //    Console.WriteLine("06:" + c + ":" + buff[0].ToString() + ":" + Encoding.Default.GetString(buff));
            //    //    break;

            //    //}
            //}

                //string ss = string.Empty;
                //while (true)
                //{
                //    //Task.Delay(1000);
                //    Thread.Sleep(500);
                //    if (_serialPort.BytesToRead > 0)
                //    {
                //        _serialPort.Read(buff, 0, buff.Length);
                //    }
                //    if (buff.Length > 0)
                //    {
                //        if (buff[0] == 0x06)
                //        {
                //            i++;
                //            SendBytes(filePaths);
                //        }
                //        //else
                //        //{
                //        //    break;
                //        //}
                //        ss = Encoding.GetEncoding("utf-8").GetString(buff);
                //    }

                //}
            }

        public void SendPacket(byte[] bys,byte h)
        {
            byte[] data;

            data = new byte[3 + bys.Length + 2];
            data[0] = 0x02;
            data[1] =  h;
            //data[2] = Convert.ToByte(~data[1]);
            data[2] = (byte)(~h);
            for (int i = 0; i < bys.Length; i++)
            {
                data[3 + i] = bys[i];
            }

            //crc16 crc1;
            byte[] crc = BitConverter.GetBytes(crc16.CRC16_ccitt(bys, 0, 1024));
            //byte[] crc = CRC16(bys.ToList());
            data[3 + bys.Length] = crc[1];
            data[3 + bys.Length + 1] = crc[0];



            _serialPort.Write(data, 0, data.Length);
        }

        private byte[] CRC16(List<byte> value, ushort poly = 0xA001, ushort crcInit = 0xFFFF)
        {
            if (value == null || !value.Any())
                throw new ArgumentException("");

            //运算
            ushort crc = crcInit;
            for (int i = 0; i < value.Count; i++)
            {
                crc = (ushort)(crc ^ (value[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);
                }
            }
            byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
            byte lo = (byte)(crc & 0x00FF);         //低位置

            return new byte[]
            {
                lo,hi
            };
        }
#if NET45
        /// <summary>
        /// 异步发送并读取数值
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="timeOut">等待超时时长</param>
        /// <param name="newLine">是否发送newline</param>
        /// <param name="value">是否一直等待读到value为止，为空则不等待</param>
        /// <returns></returns>
        public async Task<string> SendAndRead(string cmd, int timeOut, bool newLine = true, string value = "\r")
        {
            if (SendData(cmd, newLine))
            {
                var result= await PublicHelper.TaskRunning(() =>
                {
                    var len = _serialPort.BytesToRead;
                    if (len > 0)
                    {

                        return true;
                    }
                    else
                    {
                        return false;
                    }

                }, CancellationToken.None, timeOut);
                if (result) { return ReadData(value); } else
                {
                    OnLogErrorEvent("等待超时");
                    return null;
                }

            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 异步发送后一直等待返回数据不为空
        /// </summary>
        /// <param name="data"></param>
        /// <param name="timeOut"></param>
        /// <param name="newLine">是否发送新行</param>
        /// <returns></returns>
        public async Task<byte[]> SendAndRead(byte[] data, int timeOut, bool newLine = true)
        {

            if (SendData(data, newLine))
            {
                var result = await PublicHelper.TaskRunning(() =>
                {
                    var len = _serialPort.BytesToRead;
                    if (len > 0)
                    {

                        return true;
                    }
                    else
                    {
                        return false;
                    }

                }, CancellationToken.None, timeOut);
                if (result) { return ReadByte(); }
                else
                {
                    OnLogErrorEvent("等待超时");
                    return null;
                }
            }
            else
            {
                return  null;
            }
        }
#endif
    }

}
//#endif